﻿namespace UnityEditor.Controls.MenuSearchControl
{
    using System.Collections.Generic;
    using System.Linq;
    using System.Windows;
    using System.Windows.Controls;

    /// <summary>
    /// Interaction logic for MenuSearchBoxBase.xaml
    /// </summary>
    public abstract partial class MenuSearchBoxBase : TextBox
    {
        #region Constructor

        public MenuSearchBoxBase()
        {
            InitializeComponent();
        }

        public void MenuSearchBoxBase_SubmenuClosed(object sender, RoutedEventArgs e)
        {
            this.ClearResults(); //Clear output
            this.Clear(); //Clear text
        }

        /// <summary>
        /// Clears the displayed results.
        /// </summary>
        protected abstract void ClearResults();

        #endregion

        #region Abstract members

        /// <summary>
        /// Collects the items to match. Normally, these are all menu items of the containing menu.
        /// </summary>
        /// <returns>An IEnumerable&lt;Control&gt; containing the controls to match.</returns>
        protected abstract IEnumerable<Control> Collect();

        /// <summary>
        /// Displays the search results.
        /// </summary>
        /// <param name="results">The controls whose match scores were bigger than 0.
        /// The list ist descendingly sorted by the match score.</param>
        protected abstract void DisplayResults(IEnumerable<Control> results);

        /// <summary>
        /// Computes how much the control the specified search.
        /// </summary>
        /// <param name="control">The control.</param>
        /// <param name="search">The search that the user has typed.</param>
        /// <returns>A match score: The higher, the more does the control match the search. 
        /// A score of 0 means that the control does not match the search.</returns>
        protected abstract double Match(Control control, string search);

        #endregion

        #region Event handlers

        private void TextBox_Loaded(object sender, RoutedEventArgs e)
        {
            //Add handler to the parent's SubmenoClosed event;
            //can not be added in constructor because Parent property isn't initialized then yet
            (this.Parent as MenuItem).SubmenuClosed += new RoutedEventHandler(MenuSearchBoxBase_SubmenuClosed);
        }

        private void TextBox_LostFocus(object sender, RoutedEventArgs e)
        {
            //Prevent textbox from losing focus by leaving the mouse
            if (!(this.Parent as MenuItem).IsFocused) //Check if parent is focused to check if menu is being closed
                this.Focus();
        }

        private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            var results = new List<ResultInfo>();

            this.ClearResults(); //Clear output

            if (this.Text != string.Empty)
            {
                //Collect controls and iterate through them
                foreach (var control in this.Collect())
                {
                    //Compute match score; add control to result if match score is larger than 0
                    var score = this.Match(control, this.Text);
                    if (score > 0)
                        results.Add(new ResultInfo { Control = control, Score = score });
                }
            }

            //Sort result descendingly by match score
            results.Sort((x, y) =>
            {
                if (x.Score > y.Score)
                    return -1;
                else if (x.Score < y.Score)
                    return 1;
                else
                    return 0;
            });

            this.DisplayResults(from result in results select result.Control); //Display results
        }
        #endregion
    }
}
